查看原文
其他

【原创】007|搭上SpringBoot拦截器源码分析专车

亦非台 java进阶架构师 2022-04-29

专车介绍

该趟专车是开往SpringBoot拦截器源码分析的专车

什么是拦截器?拦截器就是用来拦截指定的请求,在请求之前、请求处理后做一些响应的业务逻辑处理,或者在请求完成之后做一些资源释放。

拦截器最常用的使用场景就是鉴权,在请求开始之前,对当前请求进行权限校验,如果当前请求用户具备操作当前请求的权限,就对当前请求放行,允许执行业务逻辑;否则拦截当前请求,不执行业务逻辑。

专车问题

第一个问题:如何自定义拦截器?

第二个问题:拦截器的实现原理是什么?

专车示例

第一步:实现HandlerInterceptor接口

public class SystemInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}

第二步:实现WebMvcConfigurer接口

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SystemInterceptor()).addPathPatterns("/**");
}
}

第三步:发送请求:localhost:8099/persons/,结果展示

preHandle
list
postHandle
afterCompletion

如上可以看出拦截器的定义只需要的简单的两步操作就可以完成了,定义很简单,那么为什么重写addInterceptors方法就可以定义拦截器了呢?其中的实现原理又是什么样的?接下来会带着大家一步步揭开这层神秘的面纱。

专车分析

SpringBoot请求处理源码分析一文中有提到创建RequestMappingHandlerMapping对象,下面来回顾一下。打开WebMvcAutoConfiguration自动配置类,会有对RequestMappingHandlerMapping Bean的声明

@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping();
}

创建RequestMappingHandlerMapping:WebMvcConfigurationSupport#requestMappingHandlerMapping

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 获取并设置拦截器
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}

如上可以看到获取并设置拦截器的代码,那么是如何获取拦截器的呢?

获取拦截器:WebMvcConfigurationSupport#getInterceptors

protected final Object[] getInterceptors() {
if (this.interceptors == null) {
// 创建拦截器注册器
InterceptorRegistry registry = new InterceptorRegistry();
// 新增拦截器
addInterceptors(registry);
// 添加拦截器
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
// 添加拦截器
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
// 获取拦截器列表
this.interceptors = registry.getInterceptors();
}
// 返回拦截器列表
return this.interceptors.toArray();
}

如上需要重点分析的部分就是如何往注册器中添加拦截器

往注册器中添加拦截器:DelegatingWebMvcConfiguration#addInterceptors

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

// 创建WebMvcConfigurerComposite对象
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


// 注入我们声明的WebMvcConfigurer接口实现,如上示例就是我们定义的WebMvcConfig
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
// 将定义的webmvc配置类添加到configurers对象中
this.configurers.addWebMvcConfigurers(configurers);
}
}

@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
}

先来看看将我们定义的webmvc配置类添加到configurers对象的实现,然后再来分析添加拦截器方法

添加自定义webmvc配置类:WebMvcConfigurerComposite#addWebMvcConfigurers

class WebMvcConfigurerComposite implements WebMvcConfigurer {
// 定义webmvc配置类集合
private final List<WebMvcConfigurer> delegates = new ArrayList<>();


public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
// 添加我们自定义的webmvc配置
this.delegates.addAll(configurers);
}
}
}

可以看到,该类中定义了一个webmvc配置类集合,将我们定义的webmvc配置类添加到这个集合当中,该部分操作是在程序启动的时候完成的。接下来看看添加拦截器部分代码

添加拦截器:DelegatingWebMvcConfiguration#addInterceptors

@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
// 遍历所有自定义webmvc配置类,然后调用配置类中重写的addInterceptors方法
for (WebMvcConfigurer delegate : this.delegates) {
// 调用自定义webmvc配置类中的addInterceptors,并传入注册器
delegate.addInterceptors(registry);
}
}

再来看看我们定义的webmvc配置类

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SystemInterceptor()).addPathPatterns("/**");
}
}

我们拿着拦截器注册器,并调用它的添加拦截器方法,将我们自定义的拦截器添加到了拦截器注册器中

专车回顾

第一个问题:如何定义拦截器?定义拦截器需要实现两个接口HandlerInterceptor、WebMvcConfigurer

第二个问题:拦截器的实现原理?框架对外暴露web配置接口,用户需要实现web配置接口,实现想要的逻辑。框架在启动的时候会注入所有的web配置实现,在创建RequestMappingHandlerMapping对象的同时创建拦截器注册器,然后调用web配置实现类的方法并传入拦截器注册器,从而实现拦截器的注册功能。

专车总结

  • DelegatingWebMvcConfiguration类中会注入我们自定义的webmvc配置类,然后添加到WebMvcConfigurerComposite类中的webmvc配置类集合中
  • 创建RequestMappingHandlerMapping对象
  • 遍历自定义webmvc配置类集合,调用添加拦截器方法传入拦截器注册器
  • 调用自定义webmvc配置类的添加拦截器方法
  • 将自定义拦截器添加到拦截器注册器中

本专车系列文章(点击标题可跳转)

【原创】001 | 搭上SpringBoot自动注入源码分析专车

【原创】002 | 搭上SpringBoot事务源码分析专车

【原创】003 | 搭上基于SpringBoot事务思想实战专车

【原创】004 | 搭上SpringBoot事务诡异事件分析专车

【原创】005 | 搭上SpringBoot请求处理源码分析专车

【原创】006| 搭上SpringBoot参数解析返回值处理源码分析专车


————  e n d ————

11月底了,1月份过年,跳槽涨薪的时候快到了!师长认为,现在恰恰是逐步复习的时候,有计划地去准备,会比到时候仓促上阵来的从容。

为此,师长为大家准备了三份面试宝典,适用于初中级,中高级,以及资深级工程师的水平,内容包含java基础、javaweb、各个性能优化、JVM、锁、高并发、反射、Spring原理、微服务、Zookeeper、数据库、数据结构等等。

一、初中级《java面试宝典5.0》,对标8-13K

二、中高级《350道Java面试题:整理自100+公司》,对标12-20K

三、资深《java面试突击-视频版》,对标20K+

获取方式:点“在看”,V信关注师长的小号:编程最前线并回复 面试 领取,更多前线陆续奉上。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存